﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Remoting;
using System.Text;
using System.Xml;
using XMLGener.Interfaces;
using XMLGener.Utils;
using XMLGener.Data;
namespace XMLGener.Utils
{
    /// <summary>
    /// This class manages one module instance loaded from outer DLL.
    /// </summary>
    public class XModuleDefinitionInstance:XMLGenerModuleInterface
    {
        private XMLGenerModuleInterface moduleInstance = null;

        /// <summary>
        /// Return instance defined in Assembly (dll) "a" of class "t" 
        /// </summary>
        /// <param name="a">source assembly</param>
        /// <param name="t">class type to create</param>
        /// <returns></returns>
        public static XModuleDefinitionInstance getDefinitionInstance(Assembly a, Type t)
        {
            if (!checkMethods(t))
                return null;

            XModuleDefinitionInstance result = new XModuleDefinitionInstance();
            result.ass = a;
            result.modType = t;
            if (!result.initialize())
                return null ;

            
            return result;
        }

        private Assembly ass = null;
        private Type modType = null;
        private string _name = null;
        private string _id = null;
        private string _outpuType = null;
        
        /// <summary>
        /// retrictions, that this module can accept
        /// </summary>
        public XModuleRestrictions restirction = null;

        /// <summary>
        /// name of module (used for displaying to user)
        /// </summary>
        public string name { get { return _name; } }
        
        /// <summary>
        /// Identifictaion of module (not displayed to user)
        /// </summary>
        public string id { get { return _id; } }
        
        /// <summary>
        /// Type of generated string (string,number etc.)
        /// </summary>
        public string outpuType { get { return _outpuType; } }

        /// <summary>
        /// Return name of module instance.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            if (this.name != null)
                return this.name;
            return "-- undefined --";
        }

        /// <summary>
        /// Check if methods of type are orresponding to XMLGenerModuleInterface
        /// </summary>
        /// <param name="t"> type </param>
        /// <returns>true, if type's methods are corresponding to XMLGenerModuleInterface,otherwise false</returns>
        private static bool checkMethods(Type t)
        {
            string source = t.Assembly.FullName + " > class: " + t.Name;
            MethodInfo setPar = t.GetMethod("setParameters", new Type[] { typeof(string),typeof(XMLGenerDocument)});
            MethodInfo gener = t.GetMethod("generate", new Type[] {  });
            MethodInfo getdef = t.GetMethod("getDefinition", new Type[] { });
            
            if (setPar == null || gener == null || getdef == null)
            {
                if (setPar == null)
                    XLogger.addError(source, "Method \"setParameters(string)\" is not defined");
                if (gener == null)
                    XLogger.addError(source, "Method \"generate()\" is not defined");
                if (getdef == null)
                    XLogger.addError(source, "Method \"getDefinition()\" is not defined");
                return false;
            }

            if (gener.ReturnType != typeof(string))
            {
                XLogger.addError(source, "Method \"generate()\" does not returns string");
                return false;
            }

            if (getdef.ReturnType != typeof(string))
            {
                XLogger.addError(source, "Method \"getDefinition()\" does not returns string");
                return false;
            }

            return true;
        }

        /// <summary>
        /// Create module's instance by type from dll.
        /// </summary>
        /// <returns>true if initialization success, otherwise false</returns>
        private bool initialize()
        {
            if (this.ass == null || this.modType == null)
                return false;
            this.moduleInstance = ass.CreateInstance(this.modType.FullName) as XMLGenerModuleInterface;
            if (this.moduleInstance == null)
                return false;
            XmlDocument xdoc = new XmlDocument();
            xdoc.LoadXml(this.getDefinition());
            XmlElement nameEl = XMLUtils.getSubElement(xdoc.DocumentElement, "name");
            XmlElement idEl = XMLUtils.getSubElement(xdoc.DocumentElement, "id");
            XmlElement typeEl = XMLUtils.getSubElement(xdoc.DocumentElement, "type");
            if (nameEl == null || idEl == null)
            {
                XLogger.addError(this.GetType().ToString(),
                       "Module "+this.modType.Name+ "(from "+ass.FullName+") is missing id or name" );
                return false;
            }
            this._name = nameEl.InnerText;
            this._id = idEl.InnerText;
            if (typeEl != null)
                this._outpuType = typeEl.InnerText;
            else
                this._outpuType = "any";
            this.restirction = new XModuleRestrictions(xdoc.DocumentElement);
            return true;
        }



        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public XMLGenerModuleInterface createInstanceFromDLL()
        {
            return ass.CreateInstance(this.modType.FullName) as XMLGenerModuleInterface;
        }


        #region XMLGenerModuleInterface Members

        /// <summary>
        /// Set parameters into module instance.
        /// </summary>
        /// <param name="xmlParams">setings in xml format</param>
        /// <param name="parentDocument"></param>
        public void setParameters(string xmlParams,XMLGenerDocument parentDocument)
        {
            this.moduleInstance.setParameters(xmlParams,parentDocument);
        }

        /// <summary>
        /// Get generated string from module instance
        /// </summary>
        /// <returns>generated string</returns>
        public string generate()
        {
            return this.moduleInstance.generate();
        }

        /// <summary>
        /// Get module definition and settings.
        /// </summary>
        /// <returns></returns>
        public string getDefinition()
        {
            return this.moduleInstance.getDefinition();
        }

        #endregion
    }
}
